package com.gulimall.cart.service.imp;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.common.constant.CartConstant;
import com.common.to.OrderItemTo;
import com.common.to.SkuInfoTo;
import com.common.to.SpuInfoTo;
import com.common.utils.R;
import com.gulimall.cart.Interceptor.CartInterceptor;
import com.gulimall.cart.feign.ProductFeignService;
import com.gulimall.cart.service.CartService;
import com.gulimall.cart.to.UserInfoTo;
import com.gulimall.cart.vo.CartItemVo;
import com.gulimall.cart.vo.CartVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import sun.util.resources.cldr.to.CalendarData_to_TO;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @Author Rookie-6688
 * @Description
 * @Create 2021-04-25 22:25
 */
@Service
@Slf4j
public class CartServiceImpl implements CartService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource
    private ProductFeignService productFeignService;

    /**
     * 添加购物车数据
     * @param skuId
     * @param num
     */
    @Override
    public void addItem(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        // 如果购物车原本没有对应数据，那么直接新建对象数据存储
        if (StringUtils.isEmpty(cartOps.get(skuId.toString()))){
            CartItemVo cartItemVo = new CartItemVo();
            // 1、查询 skuInfo 信息，设置基本信息
            CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {   // 这两步不需要设置顺序，所以直接使用无返回值且每个远程请求都创建一个异步请求就可以了
                R info = productFeignService.info(skuId);
                if (info.getCode() != 0) {
                    log.error("购物车调用商品基本信息出错");
                    return;
                }
                SkuInfoTo skuInfoTo = info.getData("skuInfo", new TypeReference<SkuInfoTo>() {
                });
                cartItemVo.setCount(num);
                cartItemVo.setImage(skuInfoTo.getSkuDefaultImg());
                cartItemVo.setPrice(skuInfoTo.getPrice());
                cartItemVo.setTitle(skuInfoTo.getSkuTitle());
                cartItemVo.setSkuId(skuId);
            }, threadPoolExecutor);

            // 2、查询 sku 对应的属性值字符串数组
            CompletableFuture<Void> asyncFuture = CompletableFuture.runAsync(() -> {
                R r = productFeignService.getAttrValueList(skuId);
                if (r.getCode() != 0) {
                    log.error("购物车调用商品属性值字符串出错");
                    return;
                }
                List<String> attrValueList = r.getData(new TypeReference<List<String>>() {
                });
                cartItemVo.setSkuAttrValues(attrValueList);
            }, threadPoolExecutor);

            // 3、等待上面两个全部执行完，将对象转成Json格式存入 redis
            try { CompletableFuture.allOf(asyncFuture,runAsync).get(); }catch(Exception e) {e.printStackTrace();}
            String s = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), s);
        }else{
            // 如果购物车存在数据，那么就修改其数量，再次添加
            CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
            cartItemVo.setCount(cartItemVo.getCount()+num);
            String s = JSON.toJSONString(cartItemVo);
            cartOps.put(skuId.toString(), s);
        }
    }

    /**
     * 获取购物车项数据
     * @param skuId
     * @return
     */
    @Override
    public CartItemVo getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
        return cartItemVo;
    }

    /**
     * 获取购物车对象
     * @param userInfoTo
     * @return
     */
    @Override
    public CartVo getCart(UserInfoTo userInfoTo) {
        CartVo cartVo = new CartVo();
        ArrayList<CartItemVo> itemList = new ArrayList<>();
        if(userInfoTo.getUserId() == null){
            // 说明是临时用户，那么直接返回临时用户的购物车数据
            getItemList(CartConstant.CART_REDIS_PREFIX + userInfoTo.getUserKey(),itemList);
        }else{
            //说明是登陆用户，那么先将未登录的购物车数据添加到已登录的数据中，然后再将未登录和已登录的总数据返回，最后清空未登录的数据
            ArrayList<CartItemVo> noLoginItemList = new ArrayList<>();
            getItemList(CartConstant.CART_REDIS_PREFIX + userInfoTo.getUserKey(),noLoginItemList);
            // 上面先遍历了未登陆时添加到购物车的数据，所以在这里先将其中的数据添加到用户数据中去。
            if(noLoginItemList.size()>0){
                for (CartItemVo cartItemVo : noLoginItemList) {
                    addItem(cartItemVo.getSkuId(), cartItemVo.getCount());
                }
            }
            // 将总数据添加到数组中
            getItemList(CartConstant.CART_REDIS_PREFIX + userInfoTo.getUserId().toString(), itemList);
            // 最后删除未登录的key
            stringRedisTemplate.delete(CartConstant.CART_REDIS_PREFIX + userInfoTo.getUserKey());
        }
        cartVo.setItems(itemList);
        return cartVo;
    }

    /**
     * 改变购物车项选中状态
     * @param skuId
     * @param checked
     */
    @Override
    public void checkItem(Long skuId, Short checked) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
        if (cartItemVo != null) {
            cartItemVo.setCheck(checked.intValue()==1);
        }
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItemVo));
    }

    /**
     * 添加商品数量
     * @param skuId
     * @param num
     */
    @Override
    public void countItem(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        CartItemVo cartItemVo = JSON.parseObject((String) cartOps.get(skuId.toString()), CartItemVo.class);
        if (cartItemVo != null) {
            cartItemVo.setCount(num);
        }
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItemVo));
    }

    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        cartOps.delete(skuId.toString());
    }

    @Override
    public List<OrderItemTo> getOrderItem() {
        // 1、获取购物项数据
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        ArrayList<CartItemVo> cartItemList = new ArrayList<>();
        getItemList(CartConstant.CART_REDIS_PREFIX + userInfoTo.getUserId(), cartItemList);
        // 2、获取对应的skuId 的最新价格
        List<Long> skuIdList = cartItemList.stream().map(CartItemVo::getSkuId).collect(Collectors.toList());
        R r = productFeignService.infoList(skuIdList);
        if(r.getCode() != 0){
            log.error("订单调用商品返回失败");
        }
        Map<Long,SkuInfoTo> skuInfoToMap = r.getData(new TypeReference<Map<Long,SkuInfoTo>>() {});

        // 3、获取对应的sku重量
        List<Long> spuIdList = skuInfoToMap.values().stream().map(SkuInfoTo::getSpuId).distinct().collect(Collectors.toList());
        R r1 = productFeignService.spuInfoList(spuIdList);
        if(r1.getCode() != 0){
            log.error("获取spu信息时发生异常"+r1.getCode());
        }
        Map<Long, SpuInfoTo> spuInfoToMap = r1.getData(new TypeReference<Map<Long, SpuInfoTo>>() {});

        // 封装成最终的集合
        List<OrderItemTo> orderItemToList = cartItemList.stream().map(cartItemVo -> {
            OrderItemTo orderItemTo = new OrderItemTo();
            BeanUtils.copyProperties(cartItemVo, orderItemTo);
            orderItemTo.setPrice(skuInfoToMap.get(cartItemVo.getSkuId()).getPrice());
            orderItemTo.setWeight(spuInfoToMap.get(skuInfoToMap.get(cartItemVo.getSkuId()).getSpuId()).getWeight());
            return orderItemTo;
        }).collect(Collectors.toList());
        return orderItemToList;
    }

    /**
     * 将指定key下的购物车项添加到指定的数组中
     * @param key
     * @param itemList
     */
    private void getItemList(String key,ArrayList<CartItemVo> itemList) {
        BoundHashOperations<String, Object, Object> cartOps = stringRedisTemplate.boundHashOps(key);
        List<Object> values = cartOps.values();
        if(values != null && values.size()>0){
            for (Object value : values) {
                CartItemVo cartItemVo = JSON.parseObject((String) value, CartItemVo.class);
                itemList.add(cartItemVo);
            }
        }
    }

    /**
     * 获取购物车redis操作对象
     * @return
     */
    private BoundHashOperations<String, Object, Object> getCartOps() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String redisKey = CartConstant.CART_REDIS_PREFIX;
        if(userInfoTo.getUserId() != null){
            // 不为空说明是已登录用户，那么存储redis数据的key就是 指定前缀+用户ID，否则就是 指定前缀+携带Cookie的value值
            redisKey += userInfoTo.getUserId();
        }else{
            redisKey += userInfoTo.getUserKey();
        }
        // 获取 redis key的绑定对象
        BoundHashOperations<String, Object, Object> hashOperations = stringRedisTemplate.boundHashOps(redisKey);
        return hashOperations;
    }
}
